/*
 * 百度在线网络技术（北京）有限公司拥有本软件版权2020并保留所有权利。
 * Copyright 2020, Baidu.com,Inc 2:Baidu Online Network Technology (Beijing) Co.,Ltd,
 * All rights reserved.
 */

package com.azdebugit.test.collection;

/**
 * Created by Administrator on 2019/11/28.
 */

//import com.hx.datk.cotent.Const;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;


/**
 * @Description //执行系统命令封装类
 * @Author Administrator
 * @CreateDate 2019/11/28 14:17
 * @UpdateDate 2019/11/28 14:17
 * @Version 1.0
 **/
public class ShellExec {

//    protected static final Logger logger = Log.get();

    protected static final String LINE_SEPARATOR = System.getProperty("line.separator");

    public final static String CHARSET                           = "UTF-8";
    protected String charset = CHARSET;//Const.CHARSET;
    //默认超时3分钟
    private Long timeout = 3 * 60 * 1000L;

    private List<String> stdioLines;
    private List<String> errorLines;
    private Process process;

    public ShellExec() {
    }

    public ShellExec(String charset) {
        this.charset = charset;
    }

    public interface Callback {
        void callback(Integer execStatus, List<String> stdioLines, List<String> errorLines);
    }

    /**
     * @param command: windows {"cmd.exe","/c","dir","&","type","pom.xml"}; linux  {"/bin/sh","-c","ls","-lrt"}
     * @return
     * @Description //执行命令入口
     * @Date 2019/11/28 20:57
     **/
    public void exec(Callback callback, Long timeout, String[] command) {
        Integer exitValue = runShell(timeout, command);
        callback.callback(exitValue, stdioLines, errorLines);
    }

    public void exec(Callback callback, String[] command) {
        exec(callback, 0L, command);
    }

    public void exec(String[] command) {
        exec(new DefaultCallback(), 0L, command);
    }

    private class DefaultCallback implements Callback {
        @Override
        public void callback(Integer execStatus, List<String> stdioLines, List<String> errorLines) {
            System.out.println("--------------    execStatus:  " + execStatus + "  ---------------");
            System.out.println("--------------    stdioout start  ---------------");
            if (stdioLines != null && stdioLines.size() > 0) {
                for (String line : stdioLines) {
                    System.out.println(line);
                }
            }
            System.out.println("--------------    stdioout  end  ---------------");
            System.out.println("--------------    errorout  start  ---------------");
            if (errorLines != null && errorLines.size() > 0) {
                for (String line : errorLines) {
                    System.out.println(line);
                }
            }
            System.out.println("--------------    errorout  end  ---------------");
        }
    }

    private class ErroroutWorker extends Thread {
        private final Process process;
        private CountDownLatch countDownLatch;

        public ErroroutWorker(Process process, CountDownLatch countDownLatch) {
            this.process = process;
            this.countDownLatch = countDownLatch;
        }

        @Override
        public void run() {
            BufferedReader bufferedReaderError = null;
            try {
                bufferedReaderError = new BufferedReader(new InputStreamReader(process.getErrorStream(), charset));

                String errorLine = null;
                Integer errorCount = 0;
                while ((errorLine = bufferedReaderError.readLine()) != null) {
                    errorLines.add(errorLine);
                    errorCount++;
                }
            } catch (Exception ex) {
                ex.printStackTrace();
                System.out.println("读取命令错误输出异常 ex: {}" + ex.getMessage() + ex);
                throw new RuntimeException(ex);
            } finally {
                if (bufferedReaderError != null) {
                    try {
                        bufferedReaderError.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                countDownLatch.countDown();
            }
        }
    }


    private class StdoutWorker extends Thread {
        private final Process process;
        private CountDownLatch countDownLatch;


        public StdoutWorker(Process process, CountDownLatch countDownLatch) {
            this.process = process;
            this.countDownLatch = countDownLatch;
        }

        @Override
        public void run() {
            BufferedReader bufferedReaderIn = null;
            try {
                String inLine = null;
                Integer inCount = 0;
                bufferedReaderIn = new BufferedReader(new InputStreamReader(process.getInputStream(), charset));
                while ((inLine = bufferedReaderIn.readLine()) != null) {
                    stdioLines.add(inLine);
                    inCount++;
                }
            } catch (Exception ex) {
                ex.printStackTrace();
                System.out.println("读取命令标准输出异常 ex: {}" + ex.getMessage() + ex);
                throw new RuntimeException(ex);
            } finally {
                if (bufferedReaderIn != null) {
                    try {
                        bufferedReaderIn.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                countDownLatch.countDown();
            }
        }
    }


    private int runShell(Long timeout, String[] command) {
        int exitValue = -1;
        final CountDownLatch countDownLatch = new CountDownLatch(2);
        boolean subThreadStatus = true;
        try {
            ProcessBuilder processBuilder = new ProcessBuilder(command);
            process = processBuilder.start();

            stdioLines = new ArrayList<>();
            errorLines = new ArrayList<>();
            StdoutWorker stdoutWorker = new StdoutWorker(process, countDownLatch);
            ErroroutWorker erroroutWorker = new ErroroutWorker(process, countDownLatch);
            stdoutWorker.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
                @Override
                public void uncaughtException(Thread t, Throwable e) {
                    process.destroy();
                }
            });
            erroroutWorker.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
                @Override
                public void uncaughtException(Thread t, Throwable e) {
                    process.destroy();
                }
            });
            stdoutWorker.start();
            erroroutWorker.start();

            if (timeout > 0) {
                this.timeout = timeout;
            }
            //等待process执行完成
            process.waitFor(this.timeout, TimeUnit.MILLISECONDS);
            //等待子线程执行结束 , 设定超时, 避免溢出
            countDownLatch.await(this.timeout, TimeUnit.MILLISECONDS);

            exitValue = process.exitValue();

        } catch (Exception ex) {
            ex.printStackTrace();
            System.out.println("执行命令异常 ex: {}" + ex.getMessage() + ex);
        } finally {
            if (process != null) {
                process.destroy();
            }
        }
        return exitValue;
    }


    public static void main(String[] args) {
        ShellExec shellExec = new ShellExec("utf-8");
//        ShellExec shellExec = new ShellExec("GBK2312");

        String[] command = {"cmd.exe", "/c", "ipconfig /all"};
        shellExec.exec(command);
    }
}
